package com.xk857.aspect;


import com.xk857.annotation.Log;
import com.xk857.service.LogService;
import com.xk857.utils.IpUtil;
import com.xk857.utils.RequestHolder;
import com.xk857.utils.ThrowableUtil;
import com.xk857.utils.UserAgentUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;


/**
 * 处理日志信息，将日志信息打印到数据库
 * @author CV大魔王
 * @date 2020-12-04
 */
@Component
@Aspect
public class LogAspect {

    @Autowired
    private LogService logService;

    /**
     * 防止线程冲突
     */
    ThreadLocal<Long> currentTime = new ThreadLocal<>();

    /**
     * 用于输出日志
     */
    private final static Logger logger = LoggerFactory.getLogger(LogService.class);

    /**
     * 配置切入点
     */
    @Pointcut("@annotation(com.xk857.annotation.Log)")
    public void logPointcut() {
        // 该方法无方法体,主要为了让同类中其他方法使用此切入点
    }

    /**
     * 环绕通知
     *
     * @param joinPoint join point for advice
     */
    @Around("logPointcut()")
    public Object beforeLog(ProceedingJoinPoint joinPoint) throws Throwable {
        //暂存执行时间
        currentTime.set(System.currentTimeMillis());
        Object proceed;

        proceed = joinPoint.proceed();

        Long time = System.currentTimeMillis() - currentTime.get();
        //打印日志
        logger.info("执行方法：" + getDescription(joinPoint) + "   本次请求耗时：" + time + "ms");
        currentTime.remove();//销毁线程

        HttpServletRequest request = RequestHolder.getHttpServletRequest();
        String ip = IpUtil.getIp(request);
        String browser = UserAgentUtils.getBorderName(request);
        String os = UserAgentUtils.getOsName(request);

        com.xk857.domain.Log log = new com.xk857.domain.Log("INFO", time, ip, browser, os);
        logService.save(joinPoint, log);

        return proceed;
    }

    /**
     * 配置异常通知
     *
     * @param joinPoint join point for advice
     * @param e         exception
     */
    @AfterThrowing(pointcut = "logPointcut()", throwing = "e")
    public void logAfterThrowing(JoinPoint joinPoint, Throwable e) {
        HttpServletRequest request = RequestHolder.getHttpServletRequest();
        String ip = IpUtil.getIp(request);
        String browser = UserAgentUtils.getBorderName(request);
        String os = UserAgentUtils.getOsName(request);
        Long time = System.currentTimeMillis() - currentTime.get();
        currentTime.remove();

        com.xk857.domain.Log log1 = new com.xk857.domain.Log("ERROR", time, ip, browser, os);
        logger.error("方法执行异常 -----  执行方法：" + getDescription(joinPoint) + "   本次请求耗时：" + time + "ms");

        log1.setExceptionDetail(ThrowableUtil.getStackTrace(e).getBytes());
        logService.save((ProceedingJoinPoint) joinPoint, log1);
    }


    /**
     * 获取方法名
     *
     * @return 返回执行方法名，也就是@Log中的内容
     */
    public String getDescription(JoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        Log aopLog = method.getAnnotation(Log.class);
        return aopLog.value();
    }

}
